﻿// -------------------------------------------------------------------------
//    @FileName         :    NFItemDefine.h
//    @Author           :    gaoyi
//    @Date             :    24-11-19
//    @Email            :    445267987@qq.com
//    @Module           :    NFItemDefine
//
// -------------------------------------------------------------------------

#pragma once

#include "DescStore/ItemDesc.h"
#include "NFComm/NFCore/NFServerTime.h"

namespace proto_ff
{
    class Proto_CSRoleItemNtf;
}

typedef ::google::protobuf::RepeatedPtrField<proto_ff::ItemGridCSData> FF_IG_LIST;
typedef ::google::protobuf::RepeatedPtrField<proto_ff::Item> FF_ITEM_LIST;

#define MAX_ITEM_VALUE (1 << 16) // unsigned short max

#define MAX_MONEY_TYPE_NUM 200

#define MAX_OVERLAP_ITEM  200
#define MAX_STORAGE_PAGE 1                  // 最大仓库页
#define MAX_GRID_OF_STORAGE 100             // 最大仓库格子数量
#define MAX_GRID_OF_TITLE 50                // 称号最大格子数量
#define MAX_GRID_OF_BAG 150                 // 背包最大格子数量
#define MAX_GRID_OF_SKIN 200                // 时装最大格子数量
#define MAX_GRID_OF_GEM 170                 // 宝石最大格子数量
#define MAX_GRID_OF_PARTNER_EQUIP 200       // 伙伴装备最大格子数量
#define MAX_GRID_OF_PARTNER_EQUIP_STONE 200 // 伙伴装备勋章最大格子数量
#define MAX_GRID_OF_PARTNER_TOKEN 100       // 伙伴信物最大格子数量
#define MAX_GRID_OF_BUFF_EQUIP 120          // BUFF装备最大格子数量
#define MAX_TEMP_STORAGE_SIZE 20            // 临时背包格子数量
#define MAX_QUEST_BAG_SIZE 0                // 任务物品格子数量
#define MAX_ITEM_USE_LIMIT 20               // 物品使用限制
#define MAX_MB1_BAG_SIZE 2                  // 秘宝1物品格子数量
#define MAX_MB2_BAG_SIZE 4                  // 秘宝2物品格子数量
#define MAX_MB2_BAG_LOCK_SIZE 2             // 秘宝2物品可解锁格子数量

#define MAX_ITEM_SORTER_SIZE MAX_GRID_OF_SKIN

#define MAX_DRESSED_EQUIP 9
#define MAX_DRESSED_SKIN 14
#define MAX_PARTNER_NUM 30
#define MAX_DRESSED_RUNE 0
#define MAX_DRESSED_TREASURE 0
#define MAX_DRESSED_PANTER_EQUIP 9 // 3个战斗伙伴，一个最多3件伙伴装备
#define MAX_DRESSED_BUFF_EQUIP 6 //
#define MAX_DRESSED_ITEM                                                       \
  (MAX_DRESSED_BUFF_EQUIP + MAX_DRESSED_PANTER_EQUIP + MAX_DRESSED_EQUIP +     \
   MAX_DRESSED_RUNE + MAX_DRESSED_TREASURE + MAX_DRESSED_SKIN + 1)
#define MAX_ITEM_INDEX                                                         \
  (MAX_GRID_OF_BAG * 2 + MAX_GRID_OF_SKIN + MAX_DRESSED_ITEM)

class NFGridItem
{
public:
    NFGridItem()
    {
        if (EN_OBJ_MODE_INIT == NFShmMgr::Instance()->GetCreateMode())
        {
            CreateInit();
        }
        else
        {
            ResumeInit();
        }
    }

    ~NFGridItem()
    {
    }

    NFGridItem(const NFGridItem& stData)
    {
        if (this != &stData)
        {
            CopyData(stData);
        }
    }

    NFGridItem& operator=(const NFGridItem& stData)
    {
        if (this != &stData)
        {
            CopyData(stData);
        }
        return *this;
    }

    void CopyData(const NFGridItem& stData)
    {
        m_stBase = stData.m_stBase;
        m_iItemType = stData.m_iItemType;
        m_uniExt = stData.m_uniExt;
    }

    int CreateInit()
    {
        Init();
        return 0;
    }

    int ResumeInit()
    {
        return 0;
    }

    int Init()
    {
        Reset();
        return 0;
    }

    bool IsEmpty() const
    {
        return m_stBase.id == 0 || m_stBase.value == 0;
    }

    int ToPb(proto_ff::ItemGridCSData* pData) const
    {
        CHECK_NULL(0, pData);
        m_stBase.ToPb(pData->mutable_base());
        switch (m_iItemType)
        {
            case EN_ITEM_TYPE_EQUIP:
            {
                auto pExt = m_uniExt.get_m_stData_m_stEquip();
                if (pExt)
                {
                    pExt->ToPb(pData->mutable_data_equip());
                }
            }
            break;
            case EN_ITEM_TYPE_TREASUREMAP:
            {
                auto pExt = m_uniExt.get_m_stData_m_stTreasureMap();
                if (pExt)
                {
                    pExt->ToPb(pData->mutable_data_map());
                }
            }
            break;
            case EN_ITEM_TYPE_SKIN:
            {
                auto pExt = m_uniExt.get_m_stData_m_stFashion();
                if (pExt)
                {
                    pExt->ToPb(pData->mutable_data_fashion());
                }
            }
            break;
            case EN_ITEM_TYPE_PARTNER_EQUIP:
            {
                auto pExt = m_uniExt.get_m_stData_m_stPartnerEquip();
                if (pExt)
                {
                    pExt->ToPb(pData->mutable_data_partner_equip());
                }
            }
            break;
            default:
                break;
        }
        return 0;
    }

    int FromPb(const proto_ff::ItemGridCSData& stData)
    {
        const E_Item* pDesc = ItemDesc::Instance()->GetDesc(stData.base().id());
        CHECK_EXPR(pDesc, -1, "Get Desc Failed: {}", stData.ShortDebugString());
        m_stBase.FromPb(stData.base());
        m_iItemType = pDesc->item_type;
        switch (m_iItemType)
        {
            case EN_ITEM_TYPE_EQUIP:
            {
                m_uniExt.init_which_m_stData(NFGridItemExt_m_stEquip_tag);
                auto pExt = m_uniExt.get_m_stData_m_stEquip();
                if (pExt)
                {
                    pExt->FromPb(stData.data_equip());
                }
            }
            break;
            case EN_ITEM_TYPE_TREASUREMAP:
            {
                m_uniExt.init_which_m_stData(NFGridItemExt_m_stTreasureMap_tag);
                auto pExt = m_uniExt.get_m_stData_m_stTreasureMap();
                if (pExt)
                {
                    pExt->FromPb(stData.data_map());
                }
            }
            break;
            case EN_ITEM_TYPE_SKIN:
            {
                m_uniExt.init_which_m_stData(NFGridItemExt_m_stFashion_tag);
                auto pExt = m_uniExt.get_m_stData_m_stFashion();
                if (pExt)
                {
                    pExt->FromPb(stData.data_fashion());
                }
            }
            break;
            case EN_ITEM_TYPE_PARTNER_EQUIP:
            {
                m_uniExt.init_which_m_stData(NFGridItemExt_m_stPartnerEquip_tag);
                auto pExt = m_uniExt.get_m_stData_m_stPartnerEquip();
                if (pExt)
                {
                    pExt->FromPb(stData.data_partner_equip());
                }
            }
            break;
            default:
                break;
        }
        return 0;
    }

    std::string ShortDebugString() const
    {
        std::stringstream ss;
        ss << "{";
        ss << "base:" << m_stBase.ShortDebugString() << ", ";
        ss << "item_type:" << m_iItemType << ", ";
        ss << "ext:" << m_uniExt.ShortDebugString() << ",";
        ss << "}";
        return ss.str();
    }

public:
    int GetNumber() const
    {
        return m_stBase.value;
    }

    void SetNumber(int iNumber)
    {
        m_stBase.value = iNumber;
    }

    void AddNumber(int iNumber)
    {
        m_stBase.value += iNumber;
    }

    void DecNumber(int iNumber)
    {
        m_stBase.value -= iNumber;
    }

    uint64_t GetUUID() const
    {
        return m_stBase.uuid;
    }

    void SetUUID(uint64_t ullUUID)
    {
        m_stBase.uuid = ullUUID;
    }

    int GetItemID() const
    {
        return m_stBase.id;
    }

    void SetItemID(int iResID)
    {
        m_stBase.id = iResID;
    }

    int GetQuality() const
    {
        return m_stBase.quality;
    }

    void SetQuality(int iQuality)
    {
        m_stBase.quality = iQuality;
    }

    uint32_t GetEffectTime() const
    {
        return m_stBase.effective_time;
    }

    void SetEffectTime(uint32_t dwExpireTime)
    {
        m_stBase.effective_time = dwExpireTime;
    }

    uint32_t GetTradeCoolDown() const
    {
        return m_stBase.trade_cooldown;
    }

    void SetTradeCoolDown(uint32_t dwTime)
    {
        m_stBase.trade_cooldown = dwTime;
    }

    void SetBind(bool bBind)
    {
        m_stBase.role_bind = bBind;
    }

    bool IsBind() const
    {
        return m_stBase.role_bind;
    }

    bool IsEffectTimeOut() const
    {
        if (GetEffectTime() == 0)
        {
            return false;
        }

        return NF_ADJUST_TIMENOW() > GetEffectTime();
    }

    GridItemBase& GetItemBase() { return m_stBase; }
    const GridItemBase& GetItemBase() const { return m_stBase; }

    void SetEmpty()
    {
        Reset();
    }

    void Reset()
    {
        m_iItemType = 0;
        m_stBase.Init();
        m_uniExt.Init();
    }

public:
    GridItemBase m_stBase;
    int m_iItemType;
    NFGridItemExt m_uniExt;
};

// page : 0: dressed, -1: bag, -2, temp storage, -3, quest bag, >0, storage
struct NFItemIndex
{
    NFItemIndex()
    {
        if (EN_OBJ_MODE_INIT == NFShmMgr::Instance()->GetCreateMode())
        {
            CreateInit();
        }
        else
        {
            ResumeInit();
        }
    }

    int CreateInit()
    {
        uuid = 0;
        page = 0;
        index = 0;
        return 0;
    }

    int ResumeInit()
    {
        return 0;
    }

    uint64_t uuid;
    short page;
    short index;
};

class WillAddItemData;
class SelectItemGridParam;
class WillDelItemData;
class ItemOpData;
class NFItemUtil
{
public:
    /**
     * 将游戏项转换为客户端-服务器通信数据。
     * 此函数用于将游戏中的项数据(Item)转换为用于客户端与服务器通信的协议缓冲区数据结构(proto_ff::ItemGridCSData)。
     * 它通常用于同步游戏项的状态到服务器或其他客户端。
     *
     * @param item 包含项数据的NFGridItem对象，作为转换的源。
     * @param pbData 用于存储转换结果的proto_ff::ItemGridCSData对象，作为转换的目标。
     * @param index 可选参数，表示项的索引，默认为0。用于指定项在网格中的位置。
     * @return 成功时返回0，否则返回错误代码。用于指示转换过程是否成功。
     */
    static int ItemToCSData(const NFGridItem& item, proto_ff::ItemGridCSData& pbData, int index = 0);

    /**
     * 将游戏项转换为客户端-服务器通信数据。
     * 与上一个函数类似，但目标数据结构为ItemGridCSData，而不是协议缓冲区类型。
     * 这个版本的函数提供了除协议缓冲区外的另一种数据转换方式。
     *
     * @param item 包含项数据的NFGridItem对象，作为转换的源。
     * @param stData 用于存储转换结果的ItemGridCSData对象，作为转换的目标。
     * @return 成功时返回0，否则返回错误代码。用于指示转换过程是否成功。
     */
    static int ItemToCSData(const NFGridItem& item, ItemGridCSData& stData);

    /**
     * 将游戏项转换为客户端-服务器通信数据。
     * 此函数接受指向Item对象的指针，将其转换为指向proto_ff::ItemGridCSData对象的指针。
     * 它允许在不直接操作对象的情况下进行数据转换，增加了灵活性。
     *
     * @param pItem 指向要转换的Item对象的指针，作为转换的源。
     * @param pItemCSData 指向用于存储转换结果的proto_ff::ItemGridCSData对象的指针，作为转换的目标。
     * @return 成功时返回0，否则返回错误代码。用于指示转换过程是否成功。
     */
    static int ItemToCSData(const Item* pItem, proto_ff::ItemGridCSData* pItemCSData);

    // 将ItemGridCSData数据转换为NFGridItem对象，参数为引用和指针
    static int ItemFromCSData(NFGridItem& item, const proto_ff::ItemGridCSData* pData);

    // 将ItemGridCSData数据转换为NFGridItem对象，参数为引用
    static int ItemFromCSData(NFGridItem& item, const proto_ff::ItemGridCSData& stData);

public:
    // 检查给定的NFGridItem对象是否表示一个空的格子，参数为指针
    static bool IsEmptyGrid(const NFGridItem* pData);

    // 检查给定的NFGridItem对象是否表示一个空的格子，参数为引用
    static bool IsEmptyGrid(const NFGridItem& pData);

    // 设置GridItemBase对象的属性
    static void SetToGrid(GridItemBase& base, uint32_t id, int quality, uint64_t value, uint64_t uuid, uint32_t trade_cooldown, uint32_t effective_time, bool bind);

    // 检查两个GridItemBase对象是否表示相同的物品
    static bool IsSameItem(const GridItemBase& a, const GridItemBase& b);

    // 检查GridItemBase对象与给定属性是否表示相同的物品
    static bool IsSameItem(const GridItemBase& a, uint32_t id, int quality, uint64_t uuid, uint32_t trade_cooldown, uint32_t effective_time, bool role_bind);

public:
    // 检查给定的物品类型是否为普通物品
    static bool IsNormalItem(proto_ff::enItemType eType);

    // 检查给定的物品ID是否为特殊物品，当前实现总是返回false
    static bool IsSpecialItem(int iItemId) { return false; }

    // 检查给定的物品类型是否为UUID物品
    static bool IsUUIDItem(proto_ff::enItemType eType);

    // 检查给定的物品类型和子类型是否为英雄碎片或关键材料
    static bool IsHeroPieceKeyMaterial(proto_ff::enItemType eType, int iSubType);

public:
    // 检查给定的NFItemIndex对象是否表示一个在背包中的物品
    static bool IsInBag(NFItemIndex* index_info);

    // 检查给定的NFItemIndex对象是否表示一个已装备的物品
    static bool IsDressed(NFItemIndex* index_info);

public:
    // 将NFGridItem对象转换为Equip对象
    static int ItemToEquip(NFGridItem& item, Equip& equip);

    // 将NFGridItem对象转换为TreasureMap对象
    static int ItemToMap(NFGridItem& item, TreasureMap& map);

    // 将NFGridItem对象转换为PartnerEquip对象
    static int ItemToPartnerEquip(NFGridItem& item, PartnerEquip& equip);
public:
    static int GetItemPileNumMax(int iItemId, int& iPileNumMax);
    static int GetItemPileNumMax(int iItemId);
public:
    static int CalcAddBagType(const WillAddItemData& stWillAddWareData, int enPackageOpReason, int& iBagType);
    static int CalcAddBagType(const NFGridItem& stItem, int enPackageOpReason, int& iBagType);
    static int CalcAddBagType(int iItemID, int enPackageOpReason, int& iBagType);
    static int GetSuitableBag(int iItemID, int enPackageOpReason, int& iBagType);
    static bool IsValidBagType(int iBagType);
    static bool IsValidOpBagType(int iBagType);
    static bool IsValidMoney(int iItemID);
public:
    static bool ItemCanAddToBagPage(const E_Item* pItemDesc, int iPageType);
    static int GetCanPileNumMax(const WillAddItemData& fromItem, const NFGridItem& toItem, int& iCanPileNumMax);
    static bool IsSameItem(const WillAddItemData& a, const NFGridItem& b);
    static bool IsSameItem(const SelectItemGridParam& a, const NFGridItem& b);
    static bool IsSameItemNotConsiderBind(const SelectItemGridParam& a, const NFGridItem& b);
    static bool IsBind(bool bBind, const E_Item& desc, int eBindType);

    static int GetBagTabsForDel(const WillDelItemData& stWillDelItemData, std::vector<int>& aiBagType);
    static bool IsVirtualMoney(int moneyType);
public:
    static uint64_t GetCanGainMoneyNum(int iMoneyType);

public:
    static bool ItemOpDataCompareFunForDel(const ItemOpData& left, const ItemOpData& right);
public:
    static FF_IG_LIST* MutableList(proto_ff::Proto_CSRoleItemNtf* ntf, int iPageType);
    // Proto CS:
    static void AddToNtf(const Item* pItemData, FF_ITEM_LIST* ntf);
    static void AddToNtf(const Item* pItemData, FF_IG_LIST* ntf);
    static void AddToNtf(uint32_t iID, int iQuality, uint64_t iValue, FF_ITEM_LIST* ntf);
    static void AddToNtf(uint32_t iID, int iQuality, uint64_t iValue, FF_IG_LIST* ntf);
    static void AddToNtf(const NFGridItem* pData, FF_IG_LIST* ntf, int index);
    static void AddToNtfChangeNum(const NFGridItem* pData, FF_IG_LIST* ntf, int iNum);
};

class NFItemSorter
{
public:
    NFItemSorter(int iMaxItemNum);
    ~NFItemSorter();
    int Append(NFGridItem& item);
    int Count() { return m_itemsInfo.size(); }
    int Get(int index, NFGridItem& item);
    void Sort();

private:
    void exchange(NFGridItem* list, size_t a, size_t b)
    {
        NFGridItem temp;
        memcpy(&temp, list + a, sizeof(NFGridItem));
        memcpy(list + a, list + b, sizeof(NFGridItem));
        memcpy(list + b, &temp, sizeof(NFGridItem));
    }

    static bool CompareGridItem(const NFGridItem& a, const NFGridItem& b);

private:
    NFShmVector<NFGridItem, MAX_ITEM_SORTER_SIZE> m_itemsInfo;
    int m_iMaxItemNum;
};

struct NFItemHash
{
public:
    NFItemHash()
    {
        if (EN_OBJ_MODE_INIT == NFShmMgr::Instance()->GetCreateMode())
        {
            CreateInit();
        }
        else
        {
            ResumeInit();
        }
    }

    NFItemHash(int iId, int iQuality): id(iId), quality(iQuality)
    {
    }

    int CreateInit()
    {
        id = 0;
        quality = 0;
        return 0;
    }

    int ResumeInit()
    {
        return 0;
    }


    uint32_t id;
    int32_t quality;

    bool operator==(const NFItemHash& value) const
    {
        return id == value.id && quality == value.quality;
    }

    bool operator<(const NFItemHash& value) const
    {
        if (id < value.id)
        {
            return true;
        }
        else if (id > value.id)
        {
            return false;
        }
        else
        {
            return quality < value.quality;
        }
    }
};

namespace std
{
    template <>
    struct hash<NFItemHash>
    {
        size_t operator()(const NFItemHash& value) const
        {
            return NFHash::hash_combine(value.id, value.quality);
        }
    };
}
